import sys
parent_dir = "../pygl"
sys.path.append(parent_dir)
import ctypes
import time
import OpenGL.GL as gl
import glm
import imgui
import glfw
from glfw import _GLFWwindow as GLFWwindow
import numpy as np
import pygl
from common import AppWithTrackball, Sphere, AxesHelper, GridHelper

class InstansingApp(AppWithTrackball):
    # 初始化着色器，vao vbo等
    def setupVertex(self) -> None:
        gl.glEnable(gl.GL_DEPTH_TEST)
        self.light_program = pygl.ProgramVF("intermediate/shaders/geometry.vs", 
                                      "intermediate/shaders/geometry.fs")
        self.program = pygl.ProgramVF("intermediate/shaders/instancing.vs", 
                                      "intermediate/shaders/instancing.fs")
        self.background_color = glm.vec4(0.5, 0.5, 0.5, 1.0)
        # 定义一个存放100个位置的数组
        positions = np.zeros(shape=(100, 3), dtype=np.float32)
        index = 0
        for x in range(10):
            for z in range(10):
                positions[index][0]  = x * 2.0
                positions[index][1] = 3.0
                positions[index][2] = z * 2.0
                index += 1
        # 定义一个实例化顶点属性
        self.instanced_vbo = pygl.VertexBufferObject(positions)
        self.sphere = Sphere(0.8)
        self.sphere.vao.setVertexBuffer(self.instanced_vbo, 2,  0, 
                                        3 * gl.sizeof(gl.GLfloat))
        instance_position = pygl.VertexAttribute("instance_position", 
                                    3, 3, gl.GL_FLOAT, gl.GL_FALSE, 0)
        self.sphere.vao.setVertexAttribute(2, instance_position)
        # 通知OpenGL，球体使用了实例化顶点属性
        self.sphere.vao.setBindingDivisor(2, 1)

        self.light = Sphere(0.5)
        self.light_color = glm.vec3(1.0, 1.0, 1.0)
        self.light.position = glm.vec3(20.0, 20.0, 10.0)
        self.ambient_color = glm.vec3(1.0, 1.0, 1.0)
        self.Ka = 0.4
        self.Kd = 0.9
        self.Ks = 1.0
        self.shininess = 60.0
        self.camera.far = 400
        self.camera.zoom_speed = 2.0
        self.camera.reset(glm.vec3(50.0, 50.0, 50.0), glm.vec3(0.0, 0.0, 0.0), 
                    glm.vec3(0.0, 1.0, 0.0))
        self.world_axes = AxesHelper(20.0, 5.0)
        self.grid = GridHelper(30.0, 30, glm.vec3(1.0, 1.0, 1.0), 
                               glm.vec3(0.2, 0.2, 0.2))
        self.show_world_axes = True
        self.show_grid = True
        self.imgui_window_width = 250
        self.imgui_window_height = 300

    # 配置imgui界面元素
    def showImGui(self)->None:
        imgui.begin("设置")
        if imgui.button("重置"):
            self.camera.reset(glm.vec3(50.0, 50.0, 50.0), glm.vec3(0.0, 0.0, 0.0), 
                    glm.vec3(0.0, 1.0, 0.0))
        _, self.show_world_axes = imgui.checkbox("显示三维世界坐标轴", 
                                                self.show_world_axes)
        _, self.show_grid = imgui.checkbox("显示XZ平面网格", 
                                                self.show_grid)
        imgui.end()       

    # 渲染图形
    def render(self) -> None:
        gl.glClearBufferfv(gl.GL_COLOR, 0, self.background_color.to_tuple())
        gl.glClearBufferfv(gl.GL_DEPTH, 0, 1.0)
        self.view = self.camera.getViewMatrix()
        self.projection = self.camera.getProjectionMatrix()
        # 代表灯位置的球体
        self.light_program.use()
        self.light.updateModelMatrix()
        self.light_program.setUniformMatrix4fv(0, self.light.model_matrix)
        self.light_program.setUniformMatrix4fv(1, self.view)
        self.light_program.setUniformMatrix4fv(2, self.projection)
        self.light_program.setUniform1i(3, 1)
        self.light_program.setUniform3fv(4, self.light_color)
        self.light.render()    
        
        self.program.use()
        self.program.setUniform3fv(3, glm.vec3(46.0/255.0, 99.0/255.0, 190.0/255.0))
        self.program.setUniform3fv(4, self.light_color)
        self.program.setUniform3fv(5, self.light.position)
        self.program.setUniform3fv(6, self.ambient_color)
        self.program.setUniform3fv(7, self.camera.position)
        self.program.setUniform1f(8, self.Ka)
        self.program.setUniform1f(9, self.Kd)
        self.program.setUniform1f(10, self.Ks)
        self.program.setUniform1f(11, self.shininess)
        self.sphere.updateModelMatrix()
        self.program.setUniformMatrix4fv(0, self.sphere.model_matrix)
        self.program.setUniformMatrix4fv(1, self.view)
        self.program.setUniformMatrix4fv(2, self.projection)
        # 只用调用一次实例化绘制函数
        self.sphere.vao.bind()
        gl.glDrawElementsInstanced(gl.GL_TRIANGLES, self.sphere.number_indices, 
                                   gl.GL_UNSIGNED_INT, None,  100)

        if self.show_world_axes:
            self.world_axes.updateModelMatrix
            self.world_axes.render(self.view, self.projection)
        if self.show_grid:
            self.grid.render(self.view, self.projection)

    # 释放资源
    def cleanup(self) -> None:
        self.world_axes.delete()
        self.grid.delete()
        self.sphere.delete()
        self.light.delete()
        self.program.delete()
        self.light_program.delete()
        self.instanced_vbo.delete()

if __name__ == "__main__":
    app = InstansingApp(100, 100, 800, 800, "你好，实例化球体App！")
    app.run()


